<!-- 弹出层 -->
<template>
  <view class="aster-popup">
    <aster-mask
      :show="show"
      @click="overlayClick"
      v-if="overlay"
      :duration="overlayDuration"
      :customStyle="props.overlayStyle"
      :opacity="overlayOpacity"
    />
    <uni-transition
      :show="show"
      :styles="transitionStyle"
      :mode-class="position"
      :duration="duration"
      @change="afterEnter"
      @click="clickHandler"
    >
      <view class="aster-popup__content" :style="contentStyle" @tap.stop="noop">
        <view v-if="safeAreaInsetTop" :style="safeAreaStyle" class="aster-safe-area-bar" />
        <slot></slot>
        <view
          v-if="closeable"
          @tap.stop="close"
          class="aster-popup__content__close"
          :class="['aster-popup__content__close--' + closeIconPos]"
          hover-class="aster-popup__content__close--hover"
          hover-stay-time="150"
        >
          <uni-icons type="closeempty" :color="closeableColor" size="18" bold />
        </view>
        <view
          v-if="safeAreaInsetBottom"
          class="aster-safe-bottom"
          :style="safeBottomStyle"
          :class="[!isNvue && 'aster-safe-area-inset-bottom']"
        />
      </view>
    </uni-transition>
  </view>
</template>
<script setup lang="ts">
  import { addStyle, addUnit, deepMerge } from '@/utils';
  import { computed, CSSProperties, onMounted, ref } from 'vue';
  import AsterMask from './aster-mask.vue';

  const props = defineProps({
    // 是否展示弹窗
    show: {
      type: Boolean,
      default: () => false,
    },
    // 是否显示遮罩
    overlay: {
      type: Boolean,
      default: () => true,
    },
    // 弹出的方向，可选值为 top bottom right left center
    mode: {
      type: String,
      default: () => 'bottom',
    },
    // 动画时长，单位ms
    duration: {
      type: [String, Number],
      default: () => 300,
    },
    // 是否显示关闭图标
    closeable: {
      type: Boolean,
      default: () => false,
    },
    closeableColor: {
      type: String,
      default: () => '#909399',
    },
    // 自定义遮罩的样式
    overlayStyle: {
      type: [Object, String],
      default: () => {},
    },
    // 点击遮罩是否关闭弹窗
    closeOnClickOverlay: {
      type: Boolean,
      default: () => true,
    },
    // 层级
    zIndex: {
      type: [String, Number],
      default: () => 10075,
    },
    // 是否为iPhoneX留出底部安全距离
    safeAreaInsetBottom: {
      type: Boolean,
      default: () => true,
    },
    // 是否留出顶部安全距离（状态栏高度）
    safeAreaInsetTop: {
      type: Boolean,
      default: () => false,
    },
    // 自定义关闭图标位置，top-left为左上角，top-right为右上角，bottom-left为左下角，bottom-right为右下角
    closeIconPos: {
      type: String,
      default: () => 'top-right',
    },
    // 是否显示圆角
    round: {
      type: [Boolean, String, Number],
      default: () => 0,
    },
    // mode=center，也即中部弹出时，是否使用缩放模式
    zoom: {
      type: Boolean,
      default: () => true,
    },
    // 弹窗背景色，设置为transparent可去除白色背景
    bgColor: {
      type: String,
      default: () => '',
    },
    // 遮罩的透明度，0-1之间
    overlayOpacity: {
      type: [Number, String],
      default: () => 0.5,
    },
    customStyle: {
      type: [Object, String],
      default: () => {},
    },
  });

  const emits = defineEmits(['close', 'open', 'click']);

  const isNvue = ref<boolean>(false);

  onMounted(() => {
    // #ifdef APP-NVUE
    // 标识为是否nvue
    isNvue.value = true;
    // #endif
  });

  const overlayDuration = computed(() => {
    if (typeof props.duration === 'string') {
      return Number(props.duration) + 50;
    }
    return props.duration + 50;
  });

  const transitionStyle = computed(() => {
    const style: any = {
      zIndex: props.zIndex,
      position: 'fixed',
      display: 'flex',
    };
    style[props.mode] = 0;
    if (props.mode === 'left') {
      return deepMerge(style, {
        bottom: 0,
        top: 0,
      });
    } else if (props.mode === 'right') {
      return deepMerge(style, {
        bottom: 0,
        top: 0,
      });
    } else if (props.mode === 'top') {
      return deepMerge(style, {
        left: 0,
        right: 0,
      });
    } else if (props.mode === 'bottom') {
      return deepMerge(style, {
        left: 0,
        right: 0,
      });
    } else if (props.mode === 'center') {
      return deepMerge(style, {
        alignItems: 'center',
        'justify-content': 'center',
        top: 0,
        left: 0,
        right: 0,
        bottom: 0,
      });
    }
    return deepMerge(style, {
      alignItems: 'center',
      'justify-content': 'center',
      top: 0,
      left: 0,
      right: 0,
      bottom: 0,
    });
  });

  const contentStyle = computed(() => {
    const style: any = {};
    // 通过设备信息的safeAreaInsets值来判断是否需要预留顶部状态栏和底部安全局的位置
    // 不使用css方案，是因为nvue不支持css的iPhoneX安全区查询属性
    const { safeAreaInsets } = uni.getSystemInfoSync();
    if (props.mode !== 'center') {
      style.flex = 1;
    }
    // 背景色，一般用于设置为transparent，去除默认的白色背景
    if (props.bgColor) {
      style.backgroundColor = props.bgColor;
    }
    if (props.round) {
      const value = addUnit(props.round);
      if (props.mode === 'top') {
        style.borderBottomLeftRadius = value;
        style.borderBottomRightRadius = value;
      } else if (props.mode === 'bottom') {
        style.borderTopLeftRadius = value;
        style.borderTopRightRadius = value;
      } else if (props.mode === 'center') {
        style.borderRadius = value;
      }
    }
    return deepMerge(style, addStyle(props.customStyle));
  });

  const position = computed(() => {
    if (props.mode === 'center') {
      return props.zoom ? ['fade', 'zoom-in'] : 'fade';
    }
    if (props.mode === 'left') {
      return 'slide-left';
    }
    if (props.mode === 'right') {
      return 'slide-right';
    }
    if (props.mode === 'bottom') {
      return 'slide-bottom';
    }
    if (props.mode === 'top') {
      return 'slide-top';
    }
    return 'fade';
  });

  const overlayClick = () => {
    if (props.closeOnClickOverlay) {
      emits('close');
    }
  };

  const close = (e: any) => {
    emits('close');
  };

  const afterEnter = () => {
    emits('open');
  };

  const clickHandler = () => {
    // 由于中部弹出时，其u-transition占据了整个页面相当于遮罩，此时需要发出遮罩点击事件，是否无法通过点击遮罩关闭弹窗
    if (props.mode === 'center') {
      overlayClick();
    }
    emits('click');
  };

  const noop = (e: any) => {
    e && typeof e.stopPropagation === 'function' && e.stopPropagation();
  };

  const safeAreaStyle = () => {
    const style: CSSProperties = {};
    // 状态栏高度，由于某些安卓和微信开发工具无法识别css的顶部状态栏变量，所以使用js获取的方式
    style.height = addUnit(uni.getSystemInfoSync().statusBarHeight, 'px');
    style.backgroundColor = 'transparent';
    return style;
  };

  const safeBottomStyle = () => {
    const style: CSSProperties = {};
    // #ifdef APP-NVUE
    // nvue下，高度使用js计算填充
    style.height = addUnit(uni.getSystemInfoSync().safeAreaInsets?.bottom, 'px');
    // #endif
    return style;
  };
</script>
<style lang="scss" scoped>
  view,
  scroll-view,
  swiper-item {
    display: flex;
    flex-direction: column;
    flex-shrink: 0;
    flex-grow: 0;
    flex-basis: auto;
    align-items: stretch;
    align-content: flex-start;
  }

  .aster-popup {
    flex: 1;

    &__content {
      background-color: #fff;
      position: relative;

      &--round-top {
        border-top-left-radius: 0;
        border-top-right-radius: 0;
        border-bottom-left-radius: 10px;
        border-bottom-right-radius: 10px;
      }

      &--round-left {
        border-top-left-radius: 0;
        border-top-right-radius: 10px;
        border-bottom-left-radius: 0;
        border-bottom-right-radius: 10px;
      }

      &--round-right {
        border-top-left-radius: 10px;
        border-top-right-radius: 0;
        border-bottom-left-radius: 10px;
        border-bottom-right-radius: 0;
      }

      &--round-bottom {
        border-top-left-radius: 10px;
        border-top-right-radius: 10px;
        border-bottom-left-radius: 0;
        border-bottom-right-radius: 0;
      }

      &--round-center {
        border-top-left-radius: 10px;
        border-top-right-radius: 10px;
        border-bottom-left-radius: 10px;
        border-bottom-right-radius: 10px;
      }

      &__close {
        position: absolute;

        &--hover {
          opacity: 0.4;
        }
      }

      &__close--top-left {
        top: 15px;
        left: 15px;
      }

      &__close--top-right {
        top: 15px;
        right: 15px;
      }

      &__close--bottom-left {
        bottom: 15px;
        left: 15px;
      }

      &__close--bottom-right {
        right: 15px;
        bottom: 15px;
      }
    }
  }

  .aster-safe-area-bar {
    // nvue会默认100%，如果nvue下，显式写100%的话，会导致宽度不为100%而异常
    /* #ifndef APP-NVUE */
    width: 100%;
    /* #endif */
  }

  .aster-safe-bottom {
    /* #ifndef APP-NVUE */
    width: 100%;
    /* #endif */
  }

  .aster-safe-area-inset-bottom {
    padding-bottom: 0;
    padding-bottom: constant(safe-area-inset-bottom);
    padding-bottom: env(safe-area-inset-bottom);
  }
</style>
